from decimal import Decimal
from time import strftime, gmtime
from export.distributors.stack import Stack
from export.models import B2b, B2b_Price_Rule, B2b_Category, ProductsLink, B2b_Price
from settings import db, path

import logging
import os
import chardet


logger = logging.getLogger('update_price')  # logger


class Distributor():
    """
    Class of work with export.distributors.distributors.
    """
    configDistributor = {'stek': Stack()}
    currencies = {}

    def __init__(self):
        """
        Set currency

        """
        self.getCurrency()

    def getCurrency(self):
        """
        Set currency. Get current currency from db.

        """
        for key in self.configDistributor.keys():
            self.currencies[key] = self.configDistributor[key].currency

    def updateRules(self, content, delimetr):
        """
        Update rules of creating price.

        :param content: csv file content with rows:
        :param delimetr: delimetr of csv file
        """
        self.getCurrency()
        if not delimetr:
            delimetr = ','
        rows = content.decode(chardet.detect(content)['encoding']).encode('utf-8').replace('\r', '').split('\n')[1:]
        for row in rows:
            if row:
                cells = row.split(delimetr)
                b2b = B2b.objects.get_or_create(b2b_name=cells[0])[0]
                b2bP = B2b_Price_Rule.objects.get_or_create(b2b_id=b2b,
                                                            category_name=cells[1].replace(' ', '').replace('\"', ''))[0]
                b2bP.price_level = int(cells[2].replace('\"', ''))
                b2bP.price_additionally = Decimal(cells[3].replace('\"', ''))
                b2bP.save()

    def updateAvailFromPrice(self, b2bName, productsSku):
        """
        Update available product by price MTI, Ug-Contract

        :param b2bName: mti or ug
        :param productsSku: list of product sku
        """
        b2b = B2b.objects.get_or_create(b2b_name=b2bName)[0]
        B2b_Price.objects.filter(b2b=b2b).update(avail=False)
        for sku in productsSku:
            id = self.getProductIdBySku(sku)
            if id:
                product = ProductsLink.objects.get_or_create(entity_id=id)[0]
                product.getSku = sku
                product.save()
                self.setLogInfo(product, b2b, True, None, self.chekerProductAvail(id, True), None, None, False, None)

    def chekerProductAvail(self, productId, avail):
        """
        Check if available change of product

        :param productId: product id
        :param avail: available status
        :return: True if changed available status
        """
        if avail:
            status = 1
        else:
            status = 2
        value = db.sqlQuery(
            '''SELECT * FROM firstcatalog_product_entity_int where attribute_id=%s and entity_id=%s;''' % (
                self.getAttrStatusId(), productId))
        if value[0][4] == status:
            return False
        else:
            return True

    def chekerProductPrice(self, productId, price):
        """
        Check if price changed of product

        :param productId: product id
        :param price: price value
        :return: True if changed price value
        """
        value = db.sqlQuery(
            '''SELECT * FROM firstcatalog_product_entity_decimal where attribute_id=%s and entity_id=%s;''' % (
                self.getAttrPriceId(), productId))
        if value[0][4] == price:
            return False
        else:
            return True

    def getProductIdBySku(self, sku):
        """
        Get product id by sku product

        :param sku: sku product
        :return: id product or False
        """
        var = db.sqlQuery('''SELECT * FROM firstcatalog_product_entity where sku="%s";''' % sku)
        if var:
            return var[0][0]
        else:
            return False

    def getProductSkuById(self, id):
        """
        Get product sku by product id

        :param id: product id
        :return: sku product or False
        """
        var = db.sqlQuery('''SELECT * FROM firstcatalog_product_entity where entity_id="%s";''' % id)
        if var:
            return var[0][4]
        else:
            return False

    def getSku(self):
        """
        Get different name product between stek and magento by sku


        :return: list of dictionary: name_stack, name_nadiyno, sku
        """
        products = []
        products_ = self.getAllProductIdAndSku()
        for i in range(1000):
            product_ = products_[i]
            entity = ProductsLink.objects.filter(sku=product_[1].replace('bad_sku_stack_', ''))
            if entity:
                name = B2b_Price.objects.filter(product=entity[0], b2b=B2b.objects.get_or_create(b2b_name='stek')[0])
                if name:
                    name = name[0].b2b_name
                    product = {'name_stack': name.encode('utf-8'), 'sku': product_[1].replace('bad_sku_stack_', ''),
                               'name_nadiyno': self.getProductNameById(product_[0])}
                    products.append(product)
        return products

    def getAttrIdByCode(self, code):
        """
        Get attribute id by code.

        :param code: attribute code
        :return: attribute id or False
        """
        var = db.sqlQuery('''SELECT * FROM firsteav_attribute where attribute_code='%s'
                            and entity_type_id=4;''' % code)
        if var:
            return var[0][0]
        else:
            return False

    def getProductNameById(self, id):
        """
        Get product name by id

        :param id: product id
        :return: product name
        """
        attrId = self.getAttrIdByCode('name')
        return db.sqlQuery('''SELECT value FROM firstcatalog_product_entity_varchar
                            where attribute_id=%s and entity_id=%s;''' % (attrId, id))[0][0]

    def getAllProductIdAndSku(self, categoryName=None):
        """
        Get all products id

        :param categoryName: filtering by category name
        :return: list of products id and sku
        """
        if categoryName:
            var = db.sqlQuery('''SELECT entity_id, sku FROM magento2.firstcatalog_product_entity
                where attribute_set_id=(SELECT attribute_set_id FROM magento2.firsteav_attribute_set
                where attribute_set_name='%s');''' % categoryName)
        else:
            var = db.sqlQuery('SELECT entity_id, sku FROM firstcatalog_product_entity where entity_type_id=4;')
        return var

    def updatePrice(self, categoryName=None):
        """
        Update price and available of products

        :param categoryName:  category name if only defined category of products
        """
        products = self.getAllProductIdAndSku(categoryName)
        b2b = B2b.objects.get_or_create(b2b_name='stek')[0]
        for j in range(len(products) - 1):
            product = products[j]
            categories, avail, price = Stack().search(query=product[1].replace('bad_sku_stack_', ''))
            if categories:
                category = ''
                priceCat = None
                for cat in categories.split('|'):
                    if category:
                        category += '|' + cat
                    else:
                        category = cat
                    priceRule = None
                    try:
                        priceRule = B2b_Price_Rule.objects.get(b2b_id=b2b,
                                                               category_name=category.replace(' ', ''))
                    except B2b_Price_Rule.DoesNotExist:
                        pass
                    if priceRule:
                        priceCat = priceRule
                        break
                if priceCat:
                    price = (Decimal(price[priceCat.price_level - 1].replace(',', '.')) +
                             priceCat.price_additionally) * Decimal(str(self.currencies['stek']))
                    changePrice, last_price = self.updateProductPrice(product[0], price)
                    catName = priceCat.category_name
                else:
                    price = self.getProductPricebyId(product[0])
                    changePrice = False
                    last_price = price
                    catName = ''
                changeAval = self.updateAvailProduct(product[0], avail)
                product = ProductsLink.objects.get_or_create(entity_id=product[0], sku=product[1])[0]
                self.setLogInfo(product, b2b, avail, price, changeAval, changePrice, catName , True,
                                last_price)

    def setLogInfo(self, product, b2b, avail, price, changeAval, changePrice, b2b_category, update, last_price):
        """
        Set into db log info.

        :param product: export.models.ProductsLink objects
        :param b2b: export.models.B2b objects
        :param avail: boolean, available of product
        :param price: decimal, price of product
        :param changeAval: boolean, if change product available
        :param changePrice: boolean, if change product price
        :param b2b_category: string, distributors category name
        :param update: boolean, True save to log file
        :param last_price: date of last update price
        """
        productPrice = B2b_Price.objects.get_or_create(product=product, b2b=b2b)[0]
        productPrice.last_update = strftime("%Y-%m-%d %H:%M:%S", gmtime())
        productPrice.category = self.getCategoriesIdByProductId(product.entity_id)
        productPrice.avail = avail
        productPrice.price_current = price
        productPrice.change_avail = changeAval
        productPrice.change_price = changePrice
        productPrice.save()
        if update:
            self.loggingUpdate(id=product.entity_id, category=productPrice.category, avail=productPrice.avail,
                               price=productPrice.price_current, change_avail=productPrice.change_avail,
                               change_price=productPrice.change_price, b2b_category=b2b_category, last_price=last_price)

    def getCategoriesIdByProductId(self, productId):
        """
        Get categories id where product, by product id

        :param productId: product id
        :return: string, categories id
        """
        categories_ = db.sqlQuery(
            '''SELECT * FROM firstcatalog_category_product where product_id=%s;''' % productId)
        categories = ''
        if categories_:
            for category_ in categories_:
                categories += ',%s' % category_[0]
            categories = categories[1:]
        return categories

    def getAttrStatusId(self):
        """
        Get attribute code - status, id


        :return: integer, attribute id
        """
        return db.sqlQuery('SELECT attribute_id FROM firsteav_attribute where attribute_code=\'status\';')[0][0]

    def getAttrPriceId(self):
        """
        Get attribute code - price, id


        :return: integer, attribute id
        """
        return db.sqlQuery('SELECT attribute_id FROM firsteav_attribute where attribute_code=\'price\';')[0][0]

    def updateAvailProduct(self, productId, avail):
        """
        Update product available

        :param productId: integer, product id
        :param avail: boolean, product available
        :return: boolean, True if changed status
        """
        if avail:
            status = 1
        else:
            status = 2
        value = db.sqlQuery(
            '''SELECT * FROM firstcatalog_product_entity_int where attribute_id=%s and entity_id=%s;''' % (
                self.getAttrStatusId(), productId))
        db.sqlQuery('''UPDATE firstcatalog_product_entity_int SET `value`=%s WHERE `value_id`=%s;''' % (
            status, value[0][0]))
        db.commit()
        if value[0][5] == status:
            return False
        else:
            return True

    def getProductPricebyId(self, productId):
        value = db.sqlQuery(
            '''SELECT * FROM firstcatalog_product_entity_decimal where attribute_id=%s and entity_id=%s;''' % (
                self.getAttrPriceId(), productId))
        return value[0][5]

    def updateProductPrice(self, productId, price):
        """
        Update product price

        :param productId: integer, product id
        :param price: decimal, product price
        :return: boolean, True if changed price
        """
        value = db.sqlQuery(
            '''SELECT * FROM firstcatalog_product_entity_decimal where attribute_id=%s and entity_id=%s;''' % (
                self.getAttrPriceId(), productId))
        db.sqlQuery(
            '''UPDATE firstcatalog_product_entity_decimal SET `value`=%s WHERE `value_id`=%s;''' % (
                price, value[0][0]))
        db.commit()
        if value[0][5] == price:
            return False, value[0][5]
        else:
            return True, value[0][5]

    def loggingUpdate(self, **kwargs):
        """
        Logging update info

        :param kwargs: dictionary(key, value)
        """
        log = ''
        for key in kwargs.keys():
            log += ' || %s -- %s' % (key, kwargs[key])
        logger.info(log[2:])

    def getRules(self):
        """
        Get all rules of creating price


        :return: list of dict: distributors, category, price_level, price_add, price_add, id
        """
        rules = []
        rules_ = B2b_Price_Rule.objects.all()
        for rule_ in rules_:
            rule = {'distributors': B2b.objects.get(id=rule_.b2b_id_id).b2b_name, 'category': rule_.category_name,
                    'price_level': rule_.price_level, 'price_add': rule_.price_additionally, 'id': rule_.id}
            rules.append(rule)
        return rules

    def addRule(self, data):
        """
        Add new rule of creating price

        :param data: list of dict: distributors, category, price_level, price_add, price_add
        """
        try:
            if data['distributors']:
                b2b = B2b.objects.get_or_create(b2b_name=data['distributors'])[0]
                B2b_Price_Rule(b2b_id=b2b, category_name=data['category'].replace(' ', ''),
                               price_level=data['price_level'],
                               price_additionally=data['price_add']).save()
            else:
                Exception.__init__('')
        except B2b.DoesNotExist:
            pass

    def delRule(self, id):
        """
        Delete rule of creating price

        :param id: integer, rule id
        """
        try:
            B2b_Price_Rule.objects.get(id=id).delete()
        except B2b_Price_Rule.DoesNotExist:
            pass

    def getCategories(self):
        """
        Get all categories stek with status in db


        :return: list of dict: name, new, delete
        """
        categories = []
        categories_ = B2b_Category.objects.all()
        for category_ in categories_:
            category = {'name': category_.category, 'new': '+' if category_.new else '-',
                        'delete': '+' if category_.delete else '-'}
            categories.append(category)
        return categories

    def getAllLog(self):
        """
        Get all log filename


        :return: list of string
        """
        listDir = os.listdir(path + '/log/')
        logs = []
        for dir in listDir:
            if 'update_price_' in dir:
                logs.append(dir.replace('update_price_', '').replace('.log', '').replace('-', '/'))
        return sorted(logs, reverse=True)

    def getLogInfo(self, date, filter=None):
        """
        Get log file info

        :param date: date, date of logging
        :param filter: dict, of parameter filtering
        :return: dict: products, change_price, change_avail, countAll, count, price, avail, category
        """
        content = open(os.path.join(path, 'log/update_price_%s.log' % date)).read()
        products = []
        change_price = 0
        change_avail = 0
        count_all = 0
        for line in content.split('\n'):
            product = {}
            if line:
                line = line.split('-|')[1]
                lines = line.replace(' ', '').split('||')
                for attr in lines:
                    value = attr.split('--')
                    product[value[0]] = value[1]
                product['sku'] = self.getProductSkuById(product['id'])
                product['name'] = self.getProductNameById(product['id'])
                if product not in products:
                    status = True
                    if filter:
                        status = False
                        for key in filter.keys():
                            if unicode(filter[key].encode('utf-8'), 'utf-8').lower() in unicode(str(product[key]),
                                                                                                'utf-8').lower():
                                status = True
                            else:
                                status = False
                                break

                    if product['change_price'] == 'True':
                        change_price += 1
                    if product['change_avail'] == 'True':
                        change_avail += 1
                    status_change = False
                    if product['change_price'] == 'True' or product['change_avail'] == 'True':
                        count_all += 1
                        status_change = True
                    product['status'] = status_change
                    if status and product not in products:
                        products.append(product)
        result = {'products': products, 'change_price': change_price, 'change_avail': change_avail,
                  'count_all': len(products), 'count': count_all}
        if not filter and products:
            filter = {}
            for key in products[0].keys():
                filter[key] = ''
        result['filter'] = filter
        return result
